package net.alteiar.plateform.game.view.sprite;

import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;

import javax.imageio.ImageIO;

/**
 * A resource manager for sprites in the game. Its often quite important how and
 * where you get your game resources from. In most cases it makes sense to have
 * a central resource loader that goes away, gets your resources and caches them
 * for future use.
 * <p>
 * [singleton]
 * <p>
 * 
 * @author Kevin Glass
 * @author Cody Stoutenburg
 */
public class SpriteStore {
	/** The single instance of this class */
	private static SpriteStore single = new SpriteStore();

	/**
	 * Get the single instance of this class
	 * 
	 * @return The single instance of this class
	 */
	public static SpriteStore get() {
		return single;
	}

	/** The cached sprites map, from reference to sprites instance */
	private final HashMap<String, Sprite> sprites;

	private SpriteStore() {
		sprites = new HashMap<String, Sprite>();
	}

	/**
	 * Retrieve a sprites from the store
	 * 
	 * @param ref
	 *            The reference to the image to use for the sprites
	 * @return A sprites instance containing an accelerate image of the request
	 *         reference
	 */
	public Sprite getAnimatedSprite(String ref, String ext) {
		// if we've already got the sprites in the cache
		// then just return the existing version
		Sprite sprite = sprites.get(ref);
		if (sprite != null) {
			return sprite;
		}

		Integer current = 1;

		String path = ref + current.toString() + "." + ext;
		URL url = this.getClass().getClassLoader().getResource(path);
		if (url == null) {
			fail("Can't find ref: " + ref);
		}

		// create a sprites, add it the cache then return it
		AnimatedSprite spr = new AnimatedSprite(this.loadSprite(url));

		while (url != null) {
			current++;
			path = ref + current.toString() + "." + ext;
			url = this.getClass().getClassLoader().getResource(path);
			if (url != null) {
				spr.addImage(this.loadSprite(url));
			}
		}

		sprites.put(ref, spr);
		sprite = spr;

		return sprite;
	}

	/**
	 * Retrieve a sprites from the store
	 * 
	 * @param ref
	 *            The reference to the image to use for the sprites
	 * @return A sprites instance containing an accelerate image of the request
	 *         reference
	 */
	public Sprite getSprite(String ref) {
		// if we've already got the sprites in the cache
		// then just return the existing version
		Sprite sprite = sprites.get(ref);
		if (sprite != null) {
			return sprite;
		}

		// The ClassLoader.getResource() ensures we get the sprites
		// from the appropriate place, this helps with deploying the game
		// with things like webstart. You could equally do a file look
		// up here.
		URL url = this.getClass().getClassLoader().getResource(ref);
		if (url == null) {
			fail("Can't find ref: " + ref);
		}

		// create a sprites, add it the cache then return it
		sprite = new BasicSprite(this.loadSprite(url));
		sprites.put(ref, sprite);
		return sprite;
	}

	private Image loadSprite(URL url) {
		// otherwise, go away and grab the sprites from the resource
		// loader
		BufferedImage sourceImage = null;
		try {
			// use ImageIO to read the image in
			sourceImage = ImageIO.read(url);
		} catch (IOException e) {
			fail("Failed to load: " + url.getPath());
		}

		// create an accelerated image of the right size to store our sprites in
		GraphicsConfiguration gc = GraphicsEnvironment
				.getLocalGraphicsEnvironment().getDefaultScreenDevice()
				.getDefaultConfiguration();
		Image image = gc.createCompatibleImage(sourceImage.getWidth(),
				sourceImage.getHeight(), Transparency.BITMASK);

		// draw our source image into the accelerated image
		image.getGraphics().drawImage(sourceImage, 0, 0, null);

		return image;
	}

	/**
	 * Utility method to handle resource loading failure
	 * 
	 * @param message
	 *            The message to display on failure
	 */
	private void fail(String message) {
		// we'n't available
		// we dump the message and exit the game
		System.err.println(message);
		// System.exit(0);
	}

	private static GraphicsConfiguration getDefaultConfiguration() {
		GraphicsEnvironment ge = GraphicsEnvironment
				.getLocalGraphicsEnvironment();
		GraphicsDevice gd = ge.getDefaultScreenDevice();
		return gd.getDefaultConfiguration();
	}

	private static BufferedImage resize(BufferedImage source, int destWidth,
			int destHeight, Object interpolation) {
		if (source == null)
			throw new NullPointerException("source image is NULL!");
		if (destWidth <= 0 && destHeight <= 0)
			throw new IllegalArgumentException(
					"destination width & height are both <=0!");
		int sourceWidth = source.getWidth();
		int sourceHeight = source.getHeight();
		double xScale = ((double) destWidth) / (double) sourceWidth;
		double yScale = ((double) destHeight) / (double) sourceHeight;
		if (destWidth <= 0) {
			xScale = yScale;
			destWidth = (int) Math.rint(xScale * sourceWidth);
		}
		if (destHeight <= 0) {
			yScale = xScale;
			destHeight = (int) Math.rint(yScale * sourceHeight);
		}
		GraphicsConfiguration gc = getDefaultConfiguration();
		BufferedImage result = gc.createCompatibleImage(destWidth, destHeight,
				source.getColorModel().getTransparency());
		Graphics2D g2d = null;
		try {
			g2d = result.createGraphics();
			g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
					interpolation);
			AffineTransform at = AffineTransform.getScaleInstance(xScale,
					yScale);
			g2d.drawRenderedImage(source, at);
		} finally {
			if (g2d != null)
				g2d.dispose();
		}
		return result;
	}
}
